package charts ;
import flash.display.Sprite;
import charts.series.Element;
import flash.geom.Point;
import elements.axis.XAxisLabels;
import ColorConverter.Color;
import Helper.ArrayHelper;

typedef DISTANCESTRUCT = {
	var element : Element;
	var distance_x : Float;
	var distance_y : Float;
};

typedef KEYINFOSTRUCT = {
	text:String
	, font_size:Int
	, color:Color
};

class Base /*<ValueType>*/ extends Sprite {
	
	// accessed by the Keys object to display the key
	var key:String;
	var font_size: Float;

	public var color: Int;
	public var line_width: Float;
	public var circle_size: Float;
	
	//
	// hold the Element values, for lines this is an
	// array of string Y values, for Candle it is an
	// array of string 'high,open,low,close' values,
	// for scatter it is 'x,y' etc...
	//
	public var values:Array</*ValueType*/Dynamic>;
	var axis: Float;
	
	public function new () {super();}
	
	public function get_color(): Int {
		return this.color;
	}
	
	//
	// return an array of key info objects:
	//
	public function get_keys(): Array<KEYINFOSTRUCT> {
		
		var arr:Array<KEYINFOSTRUCT> = [];
		
		// some lines may not have a key
		if( (this.font_size > 0) && (this.key != '' ) )
			arr.push( { text:this.key, font_size:this.font_size, color:this.color } );
			
		return arr;
	}
	
	//
	// whatever sets of data that *may* be attached to the right
	// Y Axis call this to see if they are attached to it or not.
	// All lines, area and bar charts call this.
	//
/*
	function which_axis_am_i_attached_to( data:Array<Dynamic>, i: Float ): Int {
		//
		// some data sets are attached to the right
		// Y axis (and min max), in the future we
		// may support many axis
		//
		if( Reflect.hasField(data,'show_y2') )
			if( Reflect.hasField(data,'show_y2') )
				if( Reflect.hasField(data,'y2_lines') )
				{
					var arr:Array<String> = data.y2_lines.split(",");
					var pos: Int = ArrayHelper.indexOf<String>(arr, Std.string(i));
						//arr.indexOf( i.toString() );
					
					if ( pos == -1 )
						return 1;
					else
						return 2;	// <-- this line found in y2_lines, so it is attached to axis 2 (right axis)
				}
				
		return 1;
	}
*/
		
	
	/**
		* may be called by main.as to make the X Axis labels
		* @return
		*/
	public function get_max_x(): Float {
		
		var max: Float = Math.NEGATIVE_INFINITY;
		//
		// count the non-mask items:
		//
		for ( i in 0...this.numChildren ) {
			if (Std.is(this.getChildAt(i),Element )) {
				
				var e:Element = cast(this.getChildAt(i),Element);
				max = Math.max( max, e.get_x() );
			}
		}

		return max;
	}
	
	public function get_min_x(): Float {
		
		var min: Float = Math.POSITIVE_INFINITY;
		//
		// count the non-mask items:
		//
		for ( i in 0...this.numChildren ) {
			if (Std.is(this.getChildAt(i),Element )) {
				
				var e:Element = cast(this.getChildAt(i),Element);
				min = Math.min( min, e.get_x() );
			}
		}

		return min;
	}
	
	//
	// this should be overriden
	//
	public function resize ( sc:ScreenCoordsBase ): Void {}
	
	public function draw ( val:String, mc:Dynamic ): Void {}
	
	
	
	
	//
	// TODO: old remove when tooltips tested
	//
	public function closest ( x: Float, y: Float ): DISTANCESTRUCT
	{
		var shortest: Float = Math.POSITIVE_INFINITY;
		var closest:Element = null;
		var dx: Float;
		
		for ( i in 0...this.numChildren ) {
		
			//
			// some of the children will will mask
			// Sprites, so filter those out:
			//
			if(Std.is(this.getChildAt(i),Element )) {
				
				var e:Element = cast(this.getChildAt(i),Element);
				e.set_tip( false );
			
				dx = Math.abs( x -e.x );
			
				if( dx < shortest )	{
					shortest = dx;
					closest = e;
				}
			}
		}
		
		var dy: Float = 0;
		if( closest )
			dy = Math.abs( y - closest.y );
			
		return { element:closest, distance_x:shortest, distance_y:dy };
	}
	
	//
	// Line and bar charts will normally only have one
	// Element at any X position, but when using Radar axis
	// you may get many at any give X location.
	//
	// Scatter charts can have many items at the same X position
	//
	public function closest_2( x: Float, y: Float ): Array<Element> {

		// get the closest Elements X value
		var _x: Float = closest_x(x);
		var _y: Float = y;
		var arr:Array<Element> = this.get_all_at_this_x_pos(_x);
		
		// tr.aces('arr.length', arr.length);
		
		var closest:Array<Element> = this.get_closest_y(arr, _y);
		var dy: Int = Std.int(Math.abs( _y - closest[0].y ));
		// NOTE: Original code called attr 'y' in Array, which is moronic
		// tr.aces('closest.length', closest.length);
		
		return closest;
	}
	
	//
	// get the X value of the closest points to the mouse
	//
	function closest_x( x: Float ): Float {
		
		var closest: Float = 100000; //Math.POSITIVE_INFINITY;
		var p:flash.geom.Point;
		var x_pos: Float;
		var dx: Float;
		
		for ( i in 0...this.numChildren ) {
		
			//
			// some of the children will will mask
			// Sprites, so filter those out:
			//
			if(Std.is(this.getChildAt(i),Element )) {
	
				var e:Element = cast(this.getChildAt(i),Element);
			
				p = e.get_mid_point();
				dx = Math.abs( x - p.x );

				if( dx < closest )	{
					closest = dx;
					x_pos = Std.int(p.x);
				}
			}
		}
		
		return x_pos;
	}
	
	//
	// get all the Elements at this X position
	// BarStack overrides this
	//
		function get_all_at_this_x_pos( x: Float ):Array<Element> {
		
		var arr = new Array<Element>();
		var p:flash.geom.Point;
		var e:Element;
		
		for ( i in 0...this.numChildren ) {
		
			// some of the children will will mask
			// Sprites, so filter those out:
			//
			if(Std.is(this.getChildAt(i),Element )) {
	
				e = cast(this.getChildAt(i),Element);
				
				//
				// Point elements are invisible by default.
				//
				// Prevent invisible points from showing tooltips
				// For scatter line area
				//if (e.visible)
				//{
					p = e.get_mid_point();
					if ( p.x == x )
						arr.push( e );
				//}
			}
		}
		
		return arr;
	}
	
	//
	// scatter charts may have many Elements in the same
	// x, y location
	//
		function get_closest_y( elements:Array<Element>, y: Float):Array<Element> {
		
		var y_min: Float = Math.POSITIVE_INFINITY;
		var dy: Float;
		var closest = new Array<Element>();
		var p:flash.geom.Point;
		var e:Element;
		
		// get min Y distance
		for( e in elements ) {
			
			p = e.get_mid_point();
			dy = Math.abs( y - p.y );
			
			y_min = Math.min( dy, y_min );
		}
		
		// select all Elements at this Y pos
		for( e in elements ) {
			
			p = e.get_mid_point();
			dy = Math.abs( y - p.y );
			if( dy == y_min )
				closest.push(e);
		}

		return closest;
	}
	
	//
	// scatter charts may have many Elements in the same
	// x, y location
	//
	public function mouse_proximity( x: Float, y: Float ): Array<Element> {
		
		var closest: Float = Math.POSITIVE_INFINITY;
		var p:flash.geom.Point;
		var i: Float;
		var e:Element;
		var mouse:flash.geom.Point = new flash.geom.Point(x, y);
		
		//
		// find the closest Elements
		//
		for ( i in 0...this.numChildren ) {
		
			// filter mask Sprites
			if(Std.is(this.getChildAt(i),Element )) {
	
				e = cast(this.getChildAt(i),Element);
				closest = Math.min( flash.geom.Point.distance(e.get_mid_point(), mouse), closest );
			}
		}
		
		//
		// grab all Elements at this distance
		//
		var close:Array<Element> = [];
		for ( i in 0...this.numChildren ) {
		
			// filter mask Sprites
			if(Std.is(this.getChildAt(i),Element )) {
	
				e = cast(this.getChildAt(i),Element);
				if ( flash.geom.Point.distance(e.get_mid_point(), mouse) == closest )
					close.push(e);
			}
		}
		
		return close;
	}
	
	
	
	//
	// this is a backup function newso if the mouse leaves the
	// movie for some reason without raising the mouse
	// out event (Std.is(this happens if the user,wizzing the mouse about))
	//
	public function mouse_out(): Void {
		for ( i in 0...this.numChildren ) {
			
			// filter out the mask elements in line charts
			if(Std.is(this.getChildAt(i),Element )) {
				
				var e:Element = cast(this.getChildAt(i),Element);
				e.set_tip(false);
			}
		}
	}
	
	
	//
	// index of item (bar, point, pie slice, horizontal bar) may be used
	// to look up its X value (bar,point) or Y value (H Bar) or used as
	// the sequence number (Pie)
	//
	function get_element( index: Int, value:Dynamic ): Element {
		return null;
	}
	
	public function add_values(): Void {
		
		// keep track of the X position (column)
		var index: Int = 0;
		
		for ( val in this.values )
		{
			var element:Element;
			
			// filter out the 'null' values
			if( val != null )
			{
				element = this.get_element( index, val );
				
				if( Reflect.hasField(this.arr,'line_mask') )
					this.addChild( this.arr.line_mask );
					
				this.addChild( this.arr );
			}
			
			index++;
		}
	}
	
	/**
		* See DynamicCollection tooltip_replace_labels
		* 
		* @param	labels
		*/
	public function tooltip_replace_labels( labels:XAxisLabels ): Void {
		for ( i in 0...this.numChildren ) {
			
			// filter out the mask elements in line charts
			if(Std.is(this.getChildAt(i),Element )) {
				
				var e:Element = cast(this.getChildAt(i),Element);
				e.tooltip_replace_labels( labels );
			}
		}
	}
	
	public function die (): Void {
		
		for ( i in 0...this.numChildren )
			if (Std.is(this.getChildAt(i),Element )) {
				
				var e:Element = cast(this.getChildAt(i),Element);
				e.die();
			}
		
		while ( this.numChildren > 0 )
			this.removeChildAt(0);
	}
}
